﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using RestSharp;
using System.Net;
using Openapi.Weibo.Model;
using System.Text.RegularExpressions;

namespace Openapi.Weibo
{
    public class OAuthAuthorization
    {
        private static String _appKey = ConfigurationManager.AppSettings["AppKey"];
        private static String _appSecret = ConfigurationManager.AppSettings["AppSecret"];
        private static String _callBackUrl = ConfigurationManager.AppSettings["CallBackUrl"];
        private Api.RestApi _accessApi;

        public OAuthAuthorization()
        {
            _accessApi = new Api.RestApi(Globals.OAuthBaseUrl);
        }
        /// <summary>
        /// 指定appkey和appsecret
        /// </summary>
        /// <param name="AppKey"></param>
        /// <param name="AppSecret"></param>
        /// <param name="RedirectUri"></param>
        public OAuthAuthorization(string AppKey, string AppSecret, string RedirectUri = null):this()
        {
            if (String.IsNullOrEmpty(AppKey) || String.IsNullOrEmpty(AppSecret))
                throw new ArgumentNullException("AppKey||AppSecret");
            else
            {
                _appKey = AppKey;
                _appSecret = AppSecret;
            }
            if (!String.IsNullOrEmpty(RedirectUri))
            {
                _callBackUrl = RedirectUri;
            }
        }

        /// <summary>
        /// 获取AppKey
        /// </summary>
        public string AppKey
        {
            get
            {
                return _appKey;
            }
        }
        /// <summary>
        /// 获取AppSecret
        /// </summary>
        public string AppSecret
        {
            get
            {
                return _appSecret;
            }
        }
        /// <summary>
        /// 获取CallBackUrl
        /// </summary>
        public string CallBackUrl
        {
            get
            {
                return _callBackUrl;
            }
        }

        /// <summary>
        /// 获取请求用户授权Token的URI
        /// </summary>
        /// <param name="response"></param>
        /// <param name="state"></param>
        /// <param name="display"></param>
        /// <returns></returns>
        public static Uri GetAuthorizeURI(ResponseType Type,String State = null,String Scope  =null, DisplayDevice Display = DisplayDevice.Default, 
            Boolean Forcelogin = false, String Language=null)
        {
            IDictionary<string, string> config = new Dictionary<string, string>()
			{
				{"client_id",_appKey},
				{"redirect_uri",_callBackUrl},
                {"response_type",Type.ToString().ToLower()},
                {"scope",Scope},
                {"state",State},
                {"display",Display.ToString().ToLower()},
                {"language",Language}
			};
            if (Forcelogin)
            {
                config.Add("forcelogin","true");
            }
            UriBuilder builder = new UriBuilder(Globals.AuthorizeUrl);
            builder.Query = Utility.BuildQueryString(config);

            return builder.Uri;
        }

        /// <summary>
        /// 获取请求用户授权Token的URL
        /// </summary>
        /// <param name="response"></param>
        /// <param name="state"></param>
        /// <param name="display"></param>
        /// <returns></returns>
        public string GetRequestTokenUrl(ResponseType Type, String State = null, String Scope = null, DisplayDevice Display = DisplayDevice.Default)
        {
            var uri = GetAuthorizeURI(Type,State, Scope, Display);
            return uri.ToString();
        }

        /// <summary>
        /// 以authorization_code方式获取授权过的Access Token
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public AccessToken GetAccessTokenByCode(string authorCode)
        {
            if (String.IsNullOrEmpty(authorCode))
                throw new ArgumentNullException("authorCode");
            return this.GetAccessToken(GrantType.AuthorizationCode, new Dictionary<string, object> { { "code", authorCode } });
        }

        /// <summary>
        /// 以password方式获取授权过的Access Token
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public AccessToken GetAccessTokenByPassword(string userName, string password)
        {
            return this.GetAccessToken(GrantType.Password, new Dictionary<string, object>{
                {"username",userName},
                {"password",password}
            });
        }

        /// <summary>
        /// 以refresh_token方式获取授权过的Access Token
        /// </summary>
        /// <param name="refreshToken"></param>
        /// <returns></returns>
        public AccessToken GetAccessTokenByRefreshToken(string refreshToken)
        {
            return this.GetAccessToken(GrantType.RefreshToken, new Dictionary<string, object>{
             {"refresh_token",refreshToken}
            });
        }
        /// <summary>
        /// 获取授权过的Access Token
        /// </summary>
        /// <param name="Type">
        /// 话说是有三种方式获取(authorization_code、password、refresh_token)
        /// authorization_code  可以
        /// refresh_token 暂时未开放
        /// password    需要申请权限
        /// </param>
        /// <param name="Params"></param>
        /// <returns></returns>
        public AccessToken GetAccessToken(GrantType Type, IDictionary<string, object> Params)
        {
            if (!Params.Keys.Contains("client_id"))
                Params.Add("client_id", _appKey);
            if (!Params.Keys.Contains("client_secret"))
                Params.Add("client_secret", _appSecret);

            switch (Type)
            {
                case GrantType.AuthorizationCode:
                    Params.Add("grant_type", "authorization_code");
                    if (!Params.Keys.Contains("code"))
                    {
                        throw new ArgumentNullException("authorCode");
                    }
                    if (!Params.Keys.Contains("redirect_uri"))
                    {
                        Params["redirect_uri"] = _callBackUrl;
                    }
                    break;
                case GrantType.Password:
                    Params.Add("grant_type", "password");
                    if (!Params.Keys.Contains("username") || !Params.Keys.Contains("password"))
                    {
                        return null;
                    }
                    break;
                case GrantType.RefreshToken:
                    Params.Add("grant_type", "refresh_token");
                    break;
                default:
                    break;
            }

            var request = new RestRequest("access_token",Method.POST);
            request.AddRangeParameter(Params.ToParameterArry());
            return _accessApi.SafeExecute<AccessToken>(request).SuccessData;
        }

        /// <summary>
        /// 查询用户access_token的授权相关信息
        /// </summary>
        /// <param name="access_token"></param>
        /// <returns></returns>
        public TokenInfo GetTokenInfo(String access_token)
        {
            var request = new RestRequest("get_token_info",Method.POST);
            request.AddParameter("access_token", access_token, ParameterType.GetOrPost);
            return _accessApi.Execute<TokenInfo>(request).Data;
        }

        /// <summary>
        /// 模拟客户端授权(js)
        /// 因为直接获取以密码方式有个蛋疼的权限问题
        /// 所以才有了这个投机倒把的方法
        /// </summary>
        /// <param name="UserName"></param>
        /// <param name="Password"></param>
        /// <param name="Access_Token">传出access_token用于保存</param>
        /// <returns></returns>
        public bool SimulateLogin(string UserName, string Password, out AccessToken Access_Token)
        {
            if (_accessApi.BaseUrl != Globals.OAuthBaseUrl)
            {
                _accessApi.BaseUrl = Globals.OAuthBaseUrl;
            }
            ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
            {
                return true;
            };
            var state = Guid.NewGuid().ToString();
            IDictionary<string, object> @params = new Dictionary<string, object> 
            {
                {"display","js"},
                {"action","submit"},
                {"ticket",String.Empty},
                {"isLoginSina",String.Empty},
                {"withOfficalFlag",0},
                {"response_type","token"},
                {"regCallback",String.Empty},
                //{"redirect_uri",this.CallBackUrl},
                {"client_id",this.AppKey},
                {"state",state},
                {"from",String.Empty},
                {"userId",UserName},
                {"passwd",Password}
            };
            IRestRequest request = new RestRequest("authorize", Method.POST);
            request.AddRangeParameter(@params.ToParameterArry());
            request.AddHeader("Referer", this.GetRequestTokenUrl(ResponseType.Code,state,null, DisplayDevice.JS));
            _accessApi.CookieContainer = new CookieContainer();
            _accessApi.FollowRedirects = true;
            var result = _accessApi.Execute(request);
            if (!String.IsNullOrEmpty(result.Content))
            {
                var pattern = "{\"state\":\"(?<state>.{0,50})\",\"access_token\":\"(?<access_token>.{0,32})\",\"remind_in\":\"(?<remind_in>\\d+)\",\"expires_in\":(?<expires_in>\\d+),\"uid\":\"(?<uid>\\d+)\"}";
                var group = Regex.Match(result.Content, pattern).Groups;
                Access_Token = new AccessToken
                {
                    access_token = group["access_token"].Value,
                    remind_in = group["remind_in"].Value,
                    expires_in = group["expires_in"].Value,
                    uid = group["uid"].Value
                };
                return true;
            }
            Access_Token = null;
            return false;
        }
    }
}
